package space.panker916.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import space.panker916.pojo.User;
import space.panker916.utils.RedisKeyUtil;

import java.util.*;

import static space.panker916.utils.Constants.ENTITY_TYPE_USER;

@Service
public class FollowService {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    // 实现关注的功能（要同时修改两个 redis zset 的状态，注意事务）
    public void follow(int userId, int entityType, int entityId) {
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

                operations.multi();
                long time = System.currentTimeMillis();
                operations.opsForZSet().add(followeeKey, entityId, time);
                operations.opsForZSet().add(followerKey, userId, time);

                return operations.exec();
            }
        });
    }

    // 实现取关的功能
    // 实现关注的功能（要同时修改两个 redis zset 的状态，注意事务）
    public void unFollow(int userId, int entityType, int entityId) {
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

                operations.multi();
                long time = System.currentTimeMillis();
                operations.opsForZSet().remove(followeeKey, entityId);
                operations.opsForZSet().remove(followerKey, userId);

                return operations.exec();
            }
        });
    }

    // 查询关注的实体的数量
    public long findFolloweeCount(int userId, int entityType) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        return redisTemplate.opsForZSet().zCard(followeeKey);
    }

    // 查询实体的粉丝的数量
    public long findFollowerCount(int entityType, int entityId) {
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
        return redisTemplate.opsForZSet().zCard(followerKey);
    }

    // 查询当前用户是否已关注当前实体
    public boolean hasFollowed(int userId, int entityType, int entityId) {
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
        return redisTemplate.opsForZSet().score(followerKey, userId) != null;
    }

    // 查询某用户关注的人的列表（这个方法是只查人，如果要查其它实体，那就再写其它方法好了）
    public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
        return getFollowList(followeeKey, offset, limit);

    }

    // 查询某用户的粉丝列表
    public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
        String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
        return getFollowList(followerKey, offset, limit);
    }

    // 返回所需要的列表
    private List<Map<String, Object>> getFollowList(String key, int offset, int limit) {
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(key, offset, offset + limit - 1);

        if (targetIds == null) return null;

        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer targetId : targetIds) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(targetId);
            map.put("user", user);
            Long score = redisTemplate.opsForZSet().score(key, targetId).longValue();
            map.put("followTime", new Date(score));

            list.add(map);
        }

        return list;
    }
}
